# 正则表达式的一些匹配规则：
# . ：用于匹配任意一个字符，如 a.c 可以匹配 abc 、aac 、akc 等
# ^ ：用于匹配以...开头的字符，如 ^abc 可以匹配 abcde 、abcc 、abcak 等
# $ ：用于匹配以...结尾的字符，如 abc$ 可以匹配 xxxabc 、123abc 等
# * ：匹配前一个字符零次或多次，如 abc* 可以匹配 ab 、abc 、abcccc 等
# + ：匹配前一个字符一次或多次，如 abc+ 可以匹配 abc 、abcc 、abcccc 等
# ? ：匹配前一个字符零次或一次，如 abc? 只能匹配到 ab 和 abc
# \ ：转义字符，比如我想匹配 a.c ，应该写成 a\.c ，否则 . 会被当成匹配字符
# | ：表示左右表达式任意匹配一个，如 aaa|bbb 可以匹配 aaa 也可以匹配 bbb
# []：匹配中括号中的任意一个字符，如 a[bc]d 可以匹配 abd 和 acd，也可以写一个范围，如 [0-9] 、[a-z] 等
# ()：被括起来的表达式将作为一个分组，如 (abc){2} 可以匹配 abcabc ，a(123|456)b 可以匹配 a123b 或 a456b
# {m}：表示匹配前一个字符m次，如 ab{2}c 可以匹配 abbc
# {m,n}：表示匹配前一个字符 m 至 n 次，如 ab{1,2}c 可以匹配 abc 或 abbc
# \d ：匹配数字，如 a\dc 可以匹配 a1c 、a2c 、a3c 等
# \D ：匹配非数字，也就是除了数字之外的任意字符或符号，如 a\Dc 可以匹配 abc 、aac 、a.c 等
# \s ：匹配空白字符，也就是匹配空格、换行符、制表符等等，如 a\sc 可以匹配 'a c' 、a\nc 、a\tc 等
# \S ：匹配非空白字符，也就是匹配空格、换行符、制表符等之外的其他任意字符或符号，如 a\Sc 表示除了 'a c' 之外都能匹配，abc 、a3c 、a.c 等
# \w ：匹配大小写字母和数字，也就是匹配 [a-zA-Z0-9] 中的字符，如 a\wc 可以匹配 abc 、aBc 、a2c 、a_c 等
# \W ：匹配非大小写字母和数字，也就是匹配大小写字母和数字之外的其他任意字符或符号，如 a\Wc 可以匹配 a.c 、a#c 、a+c 等

# \A ：匹配字符串开始
# \Z ：匹配字符串结束，如果是存在换行，只匹配到换行前的结束字符串。
# \z ：匹配字符串结束
# \G ：匹配最后匹配完成的位置。
# \b ：匹配一个单词边界，也就是指单词和空格间的位置。例如， 'er\b' 可以匹配"never" 中的 'er'，但不能匹配 "verb" 中的 'er'。
# \B ：匹配非单词边界。'er\B' 能匹配 "verb" 中的 'er'，但不能匹配 "never" 中的 'er'。
import re
# 1、匹配单个字符与数字
print(re.search('.', 'Alisa is a good gril!'))  # .可以代表任意字符，所以匹配A，结果匹配一个A
print(re.search('[0-9]', 'Alisa is a good gril 7'))  # 匹配结果为数字7
print(re.search("[tester]", "tester is a good gril 7"))  # 匹配结果为一个t
# 匹配除tester之外的任意一个字符结果为空" "
print(re.search("[^tester]", "tester is a good gril 7"))
print(re.search("[^0-9]", "tester is a good gril 7"))  # 匹配一个非数字字符结果为t
print(re.search("\d", "tester is a good gril 7"))  # 匹配结果为数字7
print(re.search("[^\d]", "tester is a good gril 7"))  # 匹配结果为一个t
print(re.search("\D", "tester is a good gril 7"))  # 匹配一个非数字字符结果为t
print(re.search("\w", "tester is a good gril 7"))  # 匹配任意一个数字字母或下划线，结果为t

print(re.findall(".", "_tester is test\nd girl 3"))  # 匹配除换行符以外的任意字符
print(re.findall(".", "_tester is test\nd girl 3", flags=re.S))  # 匹配包括换行符在内的任意字符

# 2、匹配锚字符(边界字符)
    # 1.^：行首匹配，注意^在[]中才表示不匹配集合中的字符
    # 2.\A：匹配字符串开始，它和^区别是：
    #     \A只匹配整个字符串的开头即使在re.M模式下，也不会匹配其他行的行首
    #     ^匹配的是每行的行首
    # 3.\Z：匹配字符串结束，它和$区别是：
    #     \Z只匹配整个字符串的结束即使在re.M模式下，也不会匹配其他行的行尾
    #     $匹配的是每行的行尾
print(re.search("^test", "test is a good girl"))  # 表示行首必须要是test开头
print(re.search("girl$", "test is a good girl"))  # 表示行尾必须要是girl结尾
print(re.search("\Atest", "test is a good girl"))

print(re.findall("^test", "test is a good girl\ntest is a good girl", flags=re.M))
print(re.findall("\Atest", "test is a good girl\ntest is a good girl", flags=re.M))

print(re.findall("girl$", "test is a good girl\ntest is a good girl", flags=re.M))
print(re.findall("girl\Z", "test is a good girl\ntest is a good girl", flags=re.M))

print(re.findall(r"er\b", "never"))
print(re.findall(r"er\B", "never"))
print(re.findall(r"er\b", "nerve"))
print(re.findall(r"er\B", "nerve"))

# 3、匹配多个字符
print(re.findall(r"(test)", "test"))   # 匹配小括号内的内容，()内的内容xyz作为一个整体去匹配
print(re.findall(r"y?", "ytesttest"))  # 匹配0个或1个
print(re.findall(r"y*", "ytesttest"))  # 匹配0个或任意多个，贪婪匹配
print(re.findall(r"y+", "ytesttest"))  # 匹配至少1个，贪婪匹配
print(re.findall(r"y{3}", "ytestyyytest"))  # 匹配3个y
print(re.findall(r"y{3,}", "yytestyyytest"))  # 匹配至少3个y
print(re.findall(r"y{3,6}", "yytestyyytest"))  # 匹配至少3个y，最多6个y
print(re.findall(r"(t|T)est", "tttestttttest"))  # 匹配t或T
print(re.findall(r"t|Test", "tttesttttest"))
print(re.findall(r"((t|T)est)", "tttestttttest"))

# 需求：提取tester......girl
str = "tester is a good girl!tester is a nice girl!tester is a very dddd girl"
print(re.findall(r"tester.*girl", str))
print(re.findall(r"tester.*?girl", str))  # 非贪婪
# 4、非贪婪匹配
# 1.  *?：前一个字符0次或无限次扩展，最小匹配
print(re.findall(r"tes*", "tessssssssssssss"))
print(re.findall(r"tes*?", "tessssssssssssss"))
print(re.findall(r"tess*", "tessssssssssssss"))
print(re.findall(r"tess*?", "tessssssssssssss"))
# 2.  +?：前一个字符1次或无限次扩展，最小匹配
print(re.findall(r"tes+", "tessssssssssssss"))
print(re.findall(r"tes+?", "tessssssssssssss"))
print(re.findall(r"tess+", "tessssssssssssss"))
print(re.findall(r"tess+?", "tessssssssssssss"))
# 3.  ??：前一个字符0次或1次扩展，最小匹配
print(re.findall(r"tes?", "tessssssssssssss"))
print(re.findall(r"tes??", "tessssssssssssss"))
print(re.findall(r"tess?", "tessssssssssssss"))
print(re.findall(r"tess??", "tessssssssssssss"))
# 4.  {n,m}?：扩展前一个字符n到m次，含m，最小匹配
print(re.findall(r"tes{1,2}", "tessssssssssssss"))
print(re.findall(r"tesss{1,2}", "tessssssssssssss"))
print(re.findall(r"tes{1,2}?", "tessssssssssssss"))
print(re.findall(r"tesss{1,2}?", "tessssssssssssss"))

# 正则表达式修饰符 - 可选标志
    # re.I	使匹配忽略大小写
    # re.L	做本地化识别（locale-aware）匹配
    # re.M	多行匹配，影响 ^ 和 $
    # re.S	使 . 匹配包括换行在内的所有字符
    # re.U	根据Unicode字符集解析字符。这个标志影响 \w, \W, \b, \B.
    # re.X	详细模式。该模式下正则表达式可以是多行，忽略空白字符，并可以加入注释。

# re模块：
# re模块对正则表达式的使用步骤一般有三步:
# 1、re.compile(正则表达式) 将正则表达式字符串编译为Pattern实例
# 2、用pattern实例去处理文本并获得匹配结果（比如一个Match实例）
# 3、然后用Match实例去获得信息。

# 1、几个常用的Pattern对象常用的方法：
# 注意这里的match(str,pos,endpos)这里pos和endpos代表匹配字符串的开头和结尾，默认是0和len(str)
pattern = re.compile('dsa')
mat = pattern.match('dsafewq123456')
mat1 = pattern.match('qwertdsa134456')
mat3 = pattern.match('qwertdsa34456', 5)
res = mat.group()
print(mat)
print(mat1)
print(mat3)
print(res)
# search(str,pos,endpos)是查找整个字符串匹配找到的第一个
ser = pattern.search('1erweadsafdsae')
print(ser)
print(ser.group())
# findall(str,pos,endpos)这个方法是返回所有的子串并返回一个list
pattern1 = re.compile(r'\d+')
list_res = pattern1.findall('abc1def2rst3xyz')
print(list_res)
# finditer(str,pos,endpos)找到所有匹配的子串，并返回由这些匹配结果（match object）组成的迭代器。
pattern2 = re.compile(r'\d+')
p = pattern2.finditer('abc1def2rst3xyz')
print(p)
for i in p:
    print(i)

#  2、关于match object有几个常用的方法：
# match.group(): 返回 match object 中的字符串。
pattern3 = re.compile(r'(\w+) (\w+)')
m = pattern3.match('Kobe Bryant, Lakers')
print(m)
print(m.group())
print(m.group(1))
print(m.group(2))
print(m.group(1, 2))
# match.groups()：返回由所有分组匹配到的字符串组成的 tuple。
m2 = re.match(r'(\d+)\.(\d+)', "24.1632")
print(m2.groups())
# match.start()：没有参数时，返回匹配到的字符串的起始位置。指定参数（整数）时，返回该分组匹配到的字符串的起始位置。
print(m.start())
print(m.start(2))
# match.end()：返回结束位置
print(m.end())
print(m.end(1))
# match.span([group])：返回一个二元 tuple 表示匹配到的字符串的范围，即 (start, end)。
print(m.span())
print(m.span(2))

# 3、re模块除了能搜索匹配字符串以外还可以修改字符串
# split(): 一种是用re模块直接调用，一种是pattern对象调用
pattern4 = re.compile(r'[A-Z]')
g = pattern4.split('abcDefgHijkLmnoPqrs')
print(g)
print(re.split(r'[A-Z]', 'abcDefgHijkLmnoPqrs'))

# sub()和subn(): 一种是用re模块直接调用，一种是pattern对象调用。
# sub和subn的区别在于subn返回的是一个二元tuple(返回值，替换次数)
# pattern.sub(repl, string, count=0)：count代表替换次数
# 用 repl 替换 string 中每一个匹配的子串，返回替换后的字符串。若找不到匹配，则返回原字符串。
pattern5 = re.compile(r'like', re.I)
s1 = pattern5.sub(r'love', 'I like you, do you like me?')
print(s1)
s2 = pattern5.subn(r'love', 'I like you, do you like me?')
print(s2)
def fun(m):
    return m.group().upper()
s3 = pattern5.sub(fun, 'I like you, do you like me?')
print(s3)
s4 = pattern5.subn(fun, 'I like you, do you like me?', 1)
print(s4)
# re.sub(pattern,repl,string,count=0,flags=0): flags代表匹配模式
print(re.sub(r'liKE', r'love', 'I like you, do you like me?', 1, re.I))
print(re.subn(r'likE', r'love', 'I like you, do you like me?', 3, re.I))
